{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CRTP\n",
    "\n",
    "参考：[CRTP](https://en.cppreference.com/w/cpp/language/crtp)\n",
    "\n",
    "CRTP 英文全称 Curiously Recurring Template Pattern，对应中文翻译为“奇异递归模版模式”。该变成模式在实际代码中应用比较广泛，常用于实现“静态多态”，由于其是靠模版技术在编译期实现的多态，所以有性能高的特点。在很多数学运算的库中有广泛的应用（比如用于线性代数计算的数学库 Eigen）。\n",
    "\n",
    "示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "template<typename Z>\n",
    "class Y {};\n",
    " \n",
    "class X : public Y<X> {};"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类 `X` 继承自模板类 `Y`，并使用模板参数 `Z` 进行实例化。在这个例子中，`Y` 被实例化为 `Z=X`。\n",
    "\n",
    "这种模式通常用于解决一些常见的问题，例如实现虚函数、运算符重载等。通过将共同的行为放在基类中，派生类只需要实现特定的功能，而不必重复编写相同的代码。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CRTP 在 C++ 中主要有两种用途：\n",
    "\n",
    "- 静态多态（static polymorphism）\n",
    "- 添加方法同时精简代码\n",
    "\n",
    "## 静态多态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#include <iostream>\n",
    "using namespace std;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "template <typename Child>\n",
    "struct Base\n",
    "{\n",
    "\tvoid interface()\n",
    "\t{\n",
    "\t\tstatic_cast<Child*>(this)->implementation();\n",
    "\t}\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "struct Derived : Base<Derived>\n",
    "{\n",
    "\tvoid implementation()\n",
    "\t{\n",
    "\t\tcerr << \"Derived implementation\\n\";\n",
    "\t}\n",
    "};\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Derived implementation\n"
     ]
    }
   ],
   "source": [
    "Derived d;\n",
    "d.interface();\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里基类 `Base` 为模板类，子类 `Drived` 继承自 `Base` 同时模板参数为 `Drived`，基类中有接口 `interface` 而子类中则有接口对应实现 `implementation`，基类 `interface` 中将 `this` 通过 `static_cast` 转换为模板参数类型，并调用该类型的 `implemention` 方法。由于 `Drived` 继承基类时的模板为 `Drived` 类型所以在 `static_cast` 时会转换为 `Drived` 并调用 `Drived` 的 `implemention` 方法。(注意这里采用的时 `static_cast` 而不是 `dynamic_cast`，因为只有继承了 `Base` 的类型才能调用 `interface` 且这里是向下转型，所以采用 `static_cast` 是安全的。)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过 CRTP 可以使得类具有类似于虚函数的效果，同时又没有虚函数调用时的开销（虚函数调用需要通过虚函数指针查找虚函数表进行调用），同时类的对象的体积相比使用虚函数也会减少（不需要存储虚函数指针），但是缺点是无法动态绑定。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "template<typename Child>\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "\tvoid Run()\n",
    "\t{\n",
    "\t\tstatic_cast<Child*>(this)->Run();\n",
    "\t}\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog :public Animal<Dog>\n",
    "{\n",
    "public:\n",
    "\tvoid Run()\n",
    "\t{\n",
    "\t\tcout << \"Dog Run\" << endl;\n",
    "\t}\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat :public Animal<Cat>\n",
    "{\n",
    "public:\n",
    "\tvoid Run()\n",
    "\t{\n",
    "\t\tcout << \"Cat Run\" << endl;\n",
    "\t}\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "template<typename T>\n",
    "void Action(Animal<T> &animal)\n",
    "{\n",
    "\tanimal.Run();\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog Run\n",
      "Cat Run\n"
     ]
    }
   ],
   "source": [
    "Dog dog;\n",
    "Action(dog);\n",
    "\n",
    "Cat cat;\n",
    "Action(cat);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里 `Dog` 继承自 `Animal` 且模板参数为 `Dog`，`Cat` 继承自 `Animal` 且模板参数为 `Cat`，`Animal`，`Dog`，`Cat` 中都声明了 `Run`，而 `Animal` 中的 `Run` 是通过类型转换后调用模板类型的 `Run` 方法实现的。在 `Action` 模板函数中接收 `Animal` 类型的引用(或指针)并在其中调用了 `animal` 对象的 `Run` 方法，由于这里传入的是不同的子类对象，因此 `Action` 中的 `animal` 也会有不同的行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 添加方法，减少冗余\n",
    "\n",
    "假设现在需要实现一个数学运算库，以支持 `Vector2`，`Vector3`，`Vector4` ... 等类型，如果我们将每个类分别声明并实现如下：\n",
    "\n",
    "```c++\n",
    "//Vec3\n",
    "struct Vector3\n",
    "{\n",
    "\tfloat x;\n",
    "\tfloat y;\n",
    "\tfloat z;\n",
    "\n",
    "\tVector3() = default;\n",
    "\n",
    "\tVector3(float _x, float _y, float _z);\n",
    "\n",
    "\tinline Vector3& operator+=(const Vector3& rhs);\n",
    "\tinline Vector3& operator-=(const Vector3& rhs);\n",
    "\t//....\n",
    "};\n",
    "\n",
    "inline Vector3 operator+(const Vector3& lhs, const Vector3& rhs);\n",
    "inline Vector3 operator-(const Vector3& lhs, const Vector3& rhs);\n",
    "//....\n",
    "\n",
    "//Vec2\n",
    "struct Vector2\n",
    "{\n",
    "\tfloat x;\n",
    "\tfloat y;\n",
    "\n",
    "\tVector2() = default;\n",
    "\n",
    "\tVector2(float _x, float _y);\n",
    "\n",
    "\tinline Vector2& operator+=(const Vector2& rhs);\n",
    "\tinline Vector2& operator-=(const Vector2& rhs);\n",
    "\t//....\n",
    "};\n",
    "\n",
    "inline Vector2 operator+(const Vector2& lhs, const Vector2& rhs);\n",
    "inline Vector2 operator-(const Vector2& lhs, const Vector2& rhs);\n",
    "//....\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们会发现需要为每个类型都实现 `+=`、`-=`、`++`、`--`、`+`、`-` 等运算符重载，而且每个类型的一些运算符，行为都很类似，而且可以使用其他的运算符进行实现，比如 `+=`、`-=`、`++`、`--` 都可以采用 `+`、`-` 运算符进行实现。这时我们就可以采用 CRTP 抽离出这些共同的类似方法，减少代码的冗余："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "template<typename T>\n",
    "struct VectorBase\n",
    "{\n",
    "\tT& underlying() { return static_cast<T&>(*this); }\n",
    "\tT const& underlying() const { return static_cast<T const&>(*this); }\n",
    "\n",
    "\tinline T& operator+=(const T& rhs) \n",
    "\t{ \n",
    "\t\tthis->underlying() = this->underlying() + rhs;\n",
    "\t\treturn this->underlying();\n",
    "\t}\n",
    "\n",
    "\tinline T& operator-=(const T& rhs)\n",
    "\t{\n",
    "\t\tthis->underlying() = this->underlying() - rhs;\n",
    "\t\treturn this->underlying();\n",
    "\t}\n",
    "\t\n",
    "\t//.....\n",
    "};\n",
    "\n",
    "struct Vector3 : public VectorBase<Vector3>\n",
    "{\n",
    "\tfloat x;\n",
    "\tfloat y;\n",
    "\tfloat z;\n",
    "\n",
    "\tVector3() = default;\n",
    "\n",
    "\tVector3(float _x, float _y, float _z)\n",
    "\t{\n",
    "\t\tx = _x;\n",
    "\t\ty = _y;\n",
    "\t\tz = _z;\n",
    "\t}\n",
    "};\n",
    "\n",
    "inline Vector3 operator+(const Vector3& lhs, const Vector3& rhs)\n",
    "{\n",
    "\tVector3 result;\n",
    "\tresult.x = lhs.x + rhs.x;\n",
    "\tresult.y = lhs.y + rhs.y;\n",
    "\tresult.z = lhs.z + rhs.z;\n",
    "\treturn result;\n",
    "}\n",
    "\n",
    "inline Vector3 operator-(const Vector3& lhs, const Vector3& rhs)\n",
    "{\n",
    "\tVector3 result;\n",
    "\tresult.x = lhs.x - rhs.x;\n",
    "\tresult.y = lhs.y - rhs.y;\n",
    "\tresult.z = lhs.z - rhs.z;\n",
    "\treturn result;\n",
    "}\n",
    "//......\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tVector3 v0(6.0f, 5.0f, 4.0f);\n",
    "\tVector3 v2(4.0f, 5.0f, 6.0f);\n",
    "\n",
    "\tv0 += v2;\n",
    "\tv0 -= v2;\n",
    "\n",
    "\treturn 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过把 `+=`， `-=` 等操作放到基类中并采用 `+`、`-` 运算符实现，这样一来所有继承自 `VectorBase` 的类，只要其定义了 `+`、`-` 运算符就可以自动获得 `+=`、`-=` 等运算符，这样大大的减少了代码中的冗余。\n",
    "\n",
    "在有多个类型存在相同方法，且这些方法可以借助于类的其他方法进行实现时，均可以采用 CRTP 进行精简代码。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++14",
   "language": "C++14",
   "name": "xcpp14"
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "c++",
   "version": "14"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
